﻿using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Felbot.Interaction {
    /// <summary>The Felbot Xml Document root for the standard file Commands.fbx.</summary>
    [XmlRootAttribute("FelbotCommands", Namespace = "", IsNullable = false)]
    public class XmlCommandRoot {
        /// <summary>Creates object for serialization.</summary>
        public XmlCommandRoot() { }

        /// <summary>Gets the Commands.fbx version.</summary>
        [XmlAttribute("version")]
        public double Version = 1.0;

        /// <summary>Gets the list of commands.</summary>
        [XmlArray("commands", IsNullable = true), XmlArrayItem("cmd", typeof(XmlCommand), IsNullable = false)]
        public List<XmlCommand> Commands = new List<XmlCommand>();

        /// <summary>Gets the list of flags.</summary>
        [XmlArray("flags", IsNullable = true), XmlArrayItem("flag", typeof(XmlFlag), IsNullable = false)]
        public List<XmlFlag> Flags = new List<XmlFlag>();
    }

    /// <summary>The serializable <![CDATA[<cmd>]]> tag. Contains information on the command.</summary>
    [Serializable]
    public class XmlCommand {
        /// <summary>Creates object for serialization.</summary>
        public XmlCommand() { }

        /// <summary>The command's name.</summary>
        [XmlAttribute("name")]
        public string Name;

        /// <summary>
        /// Gets or sets the script and function teh command should call separated by a ".".
        /// </summary>
        /// <example>ScriptName.functionName</example>
        [XmlAttribute("function")]
        public string Function;

        /// <summary>
        /// Gets or sets the script the command should call from the <![CDATA[<cmd function="">]]> attribute.
        /// </summary>
        [XmlIgnore]
        public string ScriptToCall {
            get {
                if (Function.Contains("."))
                    return Function.Split('.')[0];
                else
                    return null;
            }
            set {
                if (Function.Contains("."))
                    Function = value + "." + Function.Split('.')[1];
                else
                    Function = value + ".";
            }
        }

        /// <summary>
        /// Gets or sets the function the command should call from the <![CDATA[<cmd function="">]]> attribute, if valid.
        /// </summary>
        [XmlIgnore]
        public string FunctionToCall {
            get {
                if (Function.Contains("."))
                    return Function.Split('.')[1];
                else
                    return null;
            }
            set {
                if (Function.Contains("."))
                    Function = Function.Split('.')[1] + "." + value;
                else
                    Function = "." + value;
            }
        }

        /// <summary>Gets or sets the description of the command.</summary>
        [XmlElement("description", IsNullable = true)]
        public string Description;

        /// <summary>Gets a list of the aliases (command words) of the command.</summary>
        [XmlArray("aliases", IsNullable = false), XmlArrayItem("alias", typeof(string), IsNullable = false)]
        public List<string> Aliases = new List<string>();

        /// <summary>Gets an object that controls what a user needs (flags and rank) to use the command.</summary>
        [XmlElement("has", typeof(XmlCmdRequires), IsNullable = true)]
        public XmlCmdRequires Requires;

        /// <summary>Gets an objec that controls the syntax of the command (arguments/input types).</summary>
        [XmlElement("syntax", typeof(XmlCmdSyntax), IsNullable = true)]
        public XmlCmdSyntax Syntax;
    }

    /// <summary>The serializable <![CDATA[<has>]]> tag. Contains information on the requirements of a command.</summary>
    [Serializable]
    public class XmlCmdRequires {
        /// <summary>Creates object for serialization.</summary>
        public XmlCmdRequires() { }

        /// <summary>Gets the list of flags required to use the command.</summary>
        [XmlAttribute("check")]
        public XmlCmdCheckType Check;

        /// <summary>Gets the list of flags required to use the command.</summary>
        [XmlArray("flags", IsNullable = true), XmlArrayItem("flag", IsNullable = false)]
        public List<string> Flags = new List<string>();
    }

    /// <summary>The serializable <![CDATA[<syntax>]]> tag. Contains information on the format of a command.</summary>
    [Serializable]
    public class XmlCmdSyntax {
        /// <summary>Creates object for serialization.</summary>
        public XmlCmdSyntax() { }

        /// <summary>Gets the list of argument objects.</summary>
        [XmlArray("arguments", IsNullable = true), XmlArrayItem("arg", typeof(XmlCmdArgument), IsNullable = false)]
        public List<XmlCmdArgument> Arguments = new List<XmlCmdArgument>();
    }

    /// <summary>The serializable <![CDATA[<arg>]]> tag. Contains information on an argument of a command.</summary>
    [Serializable]
    public class XmlCmdArgument {
        /// <summary>Creates object for serialization.</summary>
        public XmlCmdArgument() { }

        /// <summary>Gets or sets an integer which specifies the position of the argument in the order of arguments presented.</summary>
        [XmlAttribute("order")]
        public int Order;

        /// <summary>A boolean saying that the argument described is optional or required.</summary>
        [XmlAttribute("optional")]
        public bool Optional;

        /// <summary>
        /// Gets or sets a string name of a System.Type saying what the argument must be of.
        /// The special cases
        /// "any" (any variable type), and
        /// "rest" (string variable type which includes all arguments following until
        ///    the end of the command message)
        /// are accepted.</summary>
        [XmlAttribute("valuetype")]
        public string ValueType;

        /// <summary>Gets or sets the name of the argument, for documentation.</summary>
        [XmlAttribute("name")]
        public string Name;

        /// <summary>Gets or sets the description of the argument, for documentation.</summary>
        [XmlText]
        public string Description;
    }

    /// <summary>The serializable <![CDATA[<flag>]]> tag. Contains information on a flag.</summary>
    [Serializable]
    public class XmlFlag {
        /// <summary>Creates object for serialization.</summary>
        public XmlFlag() { }

        /// <summary>Gets or sets name of the flag.</summary>
        [XmlAttribute("name")]
        public string Name;

        /// <summary>Gets or sets the description of the flag, for documentation.</summary>
        [XmlText]
        public string Description;
    }

    /// <summary>Specifies how the flags on the commands will be checked.</summary>
    public enum XmlCmdCheckType {
        /// <summary>The command will require the user to have at least one required flag.</summary>
        Or = 0,
        /// <summary>The command will require the user to have all required flags.</summary>
        And = 1,
        /// <summary>The command will require the user to have exactly one required flag.</summary>
        Xor = 2,
        /// <summary>The command will require the user to have none of the required flags.</summary>
        Not = 3
    }
}
